package com.letianpai.common.tool;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;

/**
 * Created by IntelliJ IDEA.
 * User: Administrator
 * Date: 13-1-9
 * Time: 上午10:12
 * To change this template use File | Settings | File Templates.
 */
public abstract class BeanTools {
    /**
     * **循环向上转型，获取对象的DeclareField*
     * 如向上转型到Object仍无法找到，返回null*****
     */
    protected static Field getDeclaredField(final Object object, final String fieldName) {
        for (Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
            try {
                superClass.getDeclaredField(fieldName);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
        }
        return null;
    }

    /**
     * 强行设置Field 可访问*
     */
    protected static void makeAccessible(final Field field) {
        if (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) {
            field.setAccessible(true);
        }
    }

    /**
     * 获取所有字段*
     */
    public static HashMap<Object, Object> getAllFiled(Class<?> entityClass) {
        HashMap<Object, Object> data = new HashMap<Object, Object>();
        Field[] fields = entityClass.getDeclaredFields();
        String[] fieldName = new String[fields.length];
        Class<?>[] fieldType = new Class<?>[fields.length];
        for (int i = 0; i < fields.length; i++) {
            fieldName[i] = fields[i].getName();
            fieldType[i] = fields[i].getType();
        }
        data.put("fieldName", fieldName);
        data.put("fieldType", fieldType);
        return data;
    }

    /**
     * 获取第一个泛型类***
     */
    public static Class<?> getGenericClass(Class<?> clazz) {
        return getGenericClass(clazz, 0);
    }

    /**
     * 获取泛型类
     */
    public static Class<?> getGenericClass(Class<?> clazz, int index) throws IndexOutOfBoundsException {
        Type genType = clazz.getGenericSuperclass();
        if (!(genType instanceof ParameterizedType)) {
            return Object.class;
        }
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        if (index >= params.length || index < 0) {
            throw new IndexOutOfBoundsException("Index: " + index
                    + ", Size of Parameterized Type: " + params.length);
        }
        return (Class<?>) params[index];
    }

    /**
     * 强行获取私有属性的值
     */
    public static Object getPrivateProperty(Object object, String propertyName)
            throws IllegalAccessException, NoSuchFieldException {
        Field field = object.getClass().getDeclaredField(propertyName);
        field.setAccessible(true);
        return field.get(object);
    }

    /**
     * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
     */
    public static void setFieldValue(final Object object,
                                     final String fieldName, final Object value) {
        Field field = getDeclaredField(object, fieldName);

        if (field == null) {
            throw new IllegalArgumentException("Could not find field ["
                    + fieldName + "] on target [" + object + "]");
        }
        makeAccessible(field);
        try {
            field.set(object, value);
        } catch (IllegalAccessException e) {
            // logger.error("不可能抛出的异常:{}", e.getMessage());
        }
    }

    /**
     * 强行设置私有属性的值
     */
    public static void setPrivateProperty(Object object, String propertyName,
                                          Object newValue) throws IllegalAccessException,
            NoSuchFieldException {
        Field field = object.getClass().getDeclaredField(propertyName);
        field.setAccessible(true);
        field.set(object, newValue);
    }
}
